Updated 3/9/21 , Switched COVID Tracking Project Truth to download from 11/16/2020
Attach state population
EpiCovDA_WIS$population <- NA
EpiCovDA_pt_scores$population <- NA
for (loc in unique(EpiCovDA_WIS$location)){
if (loc == "US"){
temp_pop <- as.numeric(read.csv("../state_hosp_data_2020-11-16/state_popUS.csv"))
EpiCovDA_WIS[which(EpiCovDA_WIS$location=="US"),"population"] <- temp_pop
EpiCovDA_pt_scores[which(EpiCovDA_pt_scores$location == "US"),"population"] <- temp_pop
} else {
temp_pop <- as.numeric(read.csv(sprintf("../state_hosp_data_2020-11-16/state_pop%s.csv",fips(loc, to = "Abbreviation"))))
EpiCovDA_WIS[which(EpiCovDA_WIS$location==loc),"population"] <- temp_pop
EpiCovDA_pt_scores[which(EpiCovDA_pt_scores$location == fips(loc, to = "Abbreviation")),"population"] <- temp_pop
}
}
incomplete final line found by readTableHeader on '../state_hosp_data_2020-11-16/state_popUS.csv'
statsOnPerformance <- function(all_IS,column,tar_type = "death",filterdates_TF = TRUE,
includeUS = FALSE,
USonly = FALSE){
all_targets <- c("1 wk ahead cum death","2 wk ahead cum death","3 wk ahead cum death",
"4 wk ahead cum death")
if (tar_type == "case"){
all_targets <- c("1 wk ahead cum case","2 wk ahead cum case",
"3 wk ahead cum case",
"4 wk ahead cum case")
}
if (filterdates_TF){
all_IS <- subset(all_IS,(as.Date(forecast_date)>as.Date("2020-04-19")))
}
if (!includeUS){
subset(all_IS,(location!="US"))
}
if (USonly){
all_IS <- subset(all_IS,(location=="US"))
}
stats_df <- data.frame()
temp_df <- data.frame(target = "Overall",mean = mean(all_IS[grepl(tar_type,all_IS$target),column]),
median = median(all_IS[grepl(tar_type,all_IS$target),column]))
stats_df <- rbind(stats_df,temp_df)
for (tar in all_targets){
curr_subset <- subset(all_IS,target == tar)
curr_mean <- mean(curr_subset[,column])
curr_median <- median(curr_subset[,column])
temp_df <- data.frame(target = tar, mean = curr_mean,median = curr_median)
stats_df <- rbind(stats_df,temp_df)
}
return(stats_df)
}
IS_stats_EpiCovDA <- statsOnPerformance(EpiCovDA_WIS,"WIS")
# kable(IS_stats_EpiCovDA,booktabs=TRUE)
# kable(IS_stats_COVIDhub,booktabls=TRUE)
Point Estimate Errors
Max cumulative deaths for 4 week ahead forecasts: about 28.9 per 100,000
mybreaks2 <- c(0,.5,1,5,10,15,25,Inf)
mylabels2 = c("<0.5","0.5 <= x < 1","1 <= x < 5", "5 <= x < 10", "10 <= x < 15","15 <= x < 25",">=25")
figfilename1 <- sprintf("results_figures/AE_epicovda_deaths.pdf")
pdf(figfilename1, width = 10, height = 7)
p3<- ggplot()+
geom_tile(data = EpiCovDA_pt_scores[grepl("death",EpiCovDA_pt_scores$target),],
mapping =
aes(as.Date(forecast_date),
myfips(as.character(location),to="Abbreviation"),
fill= cut(abs_error/population*10^5,breaks=mybreaks2,right=FALSE,labels=mylabels2) )) +
# scale_fill_gradient2(low = "white",mid="gold",high="firebrick3",
# trans="log10",midpoint = log10(.5),na.value = "white",breaks=mybreaks2,
# labels=mybreaks2)+
scale_fill_brewer(type="seq",palette = "YlOrRd")+
theme_bw()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
xlab("Forecast Date")+ylab("State")+ggtitle("EpiCovDA Absolute Error per 100,000 for Death Forecasts") +
labs(fill="Abs. Error per 100,000")+
facet_wrap(vars(target),scales="fixed",nrow=1)
print(p3)
dev.off()
null device
1
print(p3)

# max(EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum death",j)),"abs_error"]/EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum death",j)),"population"]*10^5)
“Heatmaps” for Cases
max IS for cases about 23752 mediant IS for cases about 193
# for (i in 1:4){
mybreaks = c(0,5,25,100,300,1000,5000,10000,Inf)
mylabels = c("<5","5 <= x < 25", "25 <= x < 100", "100 <= x < 300","300 <= x < 1000","1000 <= x < 5000", "5000 <= x < 10000",">=10000")
figfilename3 <- sprintf("results_figures/IS_epicovda_cases.pdf")
pdf(figfilename3, width = 10, height = 7)
p1 <- ggplot()+
geom_tile(data = EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),],
mapping =
aes(as.Date(forecast_date),
myfips(as.character(location),to="Abbreviation"),
fill=cut(IS_05/population*10^5,breaks=mybreaks,right=FALSE,labels=mylabels))) +
# scale_fill_gradient2(low = "white",high="firebrick3",mid="gold",trans="log10",midpoint=1,
# breaks=mybreaks,labels=mybreaks)+
scale_fill_brewer(type="seq",palette = "YlOrRd")+
theme_bw()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
xlab("Forecast Date")+ylab("State")+
ggtitle("EpiCovDA Interval Scores (alpha=0.05) for Case Forecasts")+
# ggtitle(sprintf("EpiCovDA, %s wk ahead cumulative deaths",i))+
labs(fill="IS per 100,000")+
facet_wrap(vars(target),scales="fixed",nrow=1)
print(p1)
dev.off()
null device
1
print(p1)

# }
# median(EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),"IS_05"]/EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),"population"]*10^5)
Point Estimate Errors
Max cumulative deaths for 4 week ahead forecasts: about 867 per 100,000 median about 67
mybreaks2 <- c(0,1,10,25,50,100,300,500,Inf)
mylabels2 = c("<1","1 <= x < 10","10 <= x < 25", "25 <= x < 50", "50 <= x < 100","100 <= x < 300",
"300 <= x < 500",">=500")
figfilename1 <- sprintf("results_figures/AE_epicovda_cases.pdf")
pdf(figfilename1, width = 10, height = 7)
p3<- ggplot()+
geom_tile(data = EpiCovDA_pt_scores[grepl("case",EpiCovDA_pt_scores$target),],
mapping =
aes(as.Date(forecast_date),
myfips(as.character(location),to="Abbreviation"),
fill= cut(abs_error/population*10^5,breaks=mybreaks2,right=FALSE,labels=mylabels2) )) +
# scale_fill_gradient2(low = "white",mid="gold",high="firebrick3",
# trans="log10",midpoint = log10(.5),na.value = "white",breaks=mybreaks2,
# labels=mybreaks2)+
scale_fill_brewer(type="seq",palette = "YlOrRd")+
theme_bw()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
xlab("Forecast Date")+ylab("State")+ggtitle("EpiCovDA Absolute Error per 100,000 for Case Forecasts") +
labs(fill="Abs. Error per 100,000")+
facet_wrap(vars(target),scales="fixed",nrow=1)
print(p3)
dev.off()
null device
1
print(p3)

# median(EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum case",4)),"abs_error"]/EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum case",4)),"population"]*10^5)
forecasted_locations = unique(forecasts_with_truth$location)
tar_type = "case"
for (loc in forecasted_locations){
state_num = loc #"04"
target_forecasts_with_truth <-
forecasts_with_truth[grepl(tar_type,
forecasts_with_truth$target) &
(forecasts_with_truth$location == state_num)&
as.Date(forecasts_with_truth$forecast_date)
>as.Date('2020-04-10'),]
st_ab = fips(state_num,to="Abbreviation")
if (loc == "US"){
st_ab = "US"
}
fcasts_wide <- as_tibble(target_forecasts_with_truth) %>%
filter(quantile %in% c(0.025, 0.1, 0.25, 0.5, 0.75, 0.9, 0.975)) %>%
mutate(week_ahead = as.numeric(substr(target, 0,2))) %>%
pivot_wider(names_from = quantile, names_prefix="q")
figfilename <- sprintf("results_figures/%s_%s_quant_forecasts.pdf",st_ab,tar_type)
pdf(figfilename, width = 5, height = 3)
a1 <- ggplot(fcasts_wide,aes(x = as.Date(target_end_date)))+
geom_line(aes(y=q0.5, color=forecast_date, group=forecast_date)) +
# geom_ribbon(aes(ymin=q0.1, ymax=q0.9, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_ribbon(aes(ymin=q0.025, ymax=q0.975, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_ribbon(aes(ymin=q0.25, ymax=q0.75, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_point(aes(y=truth)) +
geom_line(aes(y=truth)) +
theme_bw() + xlab("Date") +
theme(legend.position = "none") + ylab(sprintf("cumulative %s",paste0(tar_type,"s"))) +
ggtitle(sprintf("Cumulative %s in %s, observed and forecasted",paste0(tar_type,"s"),st_ab))
#+
# coord_cartesian(ylim=c(250, 2500))
print(a1)
dev.off()
print(a1)
}





















































forecasted_locations = unique(forecasts_with_truth$location)
tar_type = "death"
for (loc in forecasted_locations){
state_num = loc #"04"
target_forecasts_with_truth <-
forecasts_with_truth[grepl(tar_type,
forecasts_with_truth$target) &
(forecasts_with_truth$location == state_num)&
as.Date(forecasts_with_truth$forecast_date)
>as.Date('2020-04-10'),]
st_ab = fips(state_num,to="Abbreviation")
if (loc == "US"){
st_ab = "US"
}
fcasts_wide <- as_tibble(target_forecasts_with_truth) %>%
filter(quantile %in% c(0.025, 0.1, 0.25, 0.5, 0.75, 0.9, 0.975)) %>%
mutate(week_ahead = as.numeric(substr(target, 0,2))) %>%
pivot_wider(names_from = quantile, names_prefix="q")
figfilename <- sprintf("results_figures/%s_%s_quant_forecasts.pdf",st_ab,tar_type)
pdf(figfilename, width = 5, height = 3)
a1 <- ggplot(fcasts_wide,aes(x = as.Date(target_end_date)))+
geom_line(aes(y=q0.5, color=forecast_date, group=forecast_date)) +
# geom_ribbon(aes(ymin=q0.1, ymax=q0.9, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_ribbon(aes(ymin=q0.025, ymax=q0.975, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_ribbon(aes(ymin=q0.25, ymax=q0.75, fill=forecast_date, group=forecast_date), alpha=.3) +
geom_point(aes(y=truth)) +
geom_line(aes(y=truth)) +
theme_bw() + xlab("Date") +
theme(legend.position = "none") + ylab(sprintf("cumulative %s",paste0(tar_type,"s"))) +
ggtitle(sprintf("Cumulative %s in %s, observed and forecasted",paste0(tar_type,"s"),st_ab))
#+
# coord_cartesian(ylim=c(250, 2500))
print(a1)
dev.off()
print(a1)
}





















































get_calibration_prop <- function(forecasts_with_truth,alpha_level,conditions,target_type="death"){
forecast_sub0 <- forecasts_with_truth[grepl(target_type,forecasts_with_truth$target),]
forecast_subset <- subset(forecast_sub0,eval(conditions))
lbounds = subset(forecast_subset,quantile==alpha_level/2,select="value")$value
ubounds = subset(forecast_subset,quantile==1 - alpha_level/2,select="value")$value
truth = subset(forecast_subset,quantile==0.5,select = "truth")$truth
in_bounds <- ((ubounds - truth)>=0) & ((lbounds-truth)<=0)
prop = sum(in_bounds)/length(truth)
check_df <- cbind(lbounds,ubounds,truth,in_bounds)
return(prop)
}
Calibration figures
for (i in 1:4){
curr_loc = "US"
cond_target = sprintf("%s wk ahead cum case",i)
condition = expression((target==cond_target)&(location=="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("US - %s week ahead cumulative cases",i)
curr_loc_fips = fips(curr_loc)
if (curr_loc == "US"){
curr_loc_fips = "US"
}
if (curr_loc_fips < 10){
curr_loc_fips = paste0(0,curr_loc_fips)
}else{curr_loc_fips = as.character(curr_loc_fips)}
# condition = expression((location==curr_loc_fips))
all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition,tar_type)
all_props <- c(all_props,curr_prop)
}
calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)
figfilename_calibration <- sprintf("results_figures/US_%swkaheadcases_calibration.pdf",i)
pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop))+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle(title_text)
print(a3)
dev.off()
}
for (i in 1:4){
cond_target = sprintf("%s wk ahead cum case",i)
condition = expression((target==cond_target)&(location!="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("State-level - %s week ahead cumulative cases",i)
all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition,tar_type)
all_props <- c(all_props,curr_prop)
}
calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)
figfilename_calibration <- sprintf("results_figures/state-level_%swkaheadcases_calibration.pdf",i)
pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop))+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle(title_text)
print(a3)
dev.off()
}
for (i in 1:4){
curr_loc = "US"
cond_target = sprintf("%s wk ahead cum death",i)
condition = expression((target==cond_target)&(location=="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("US - %s week ahead cumulative deaths",i)
curr_loc_fips = fips(curr_loc)
if (curr_loc == "US"){
curr_loc_fips = "US"
}
if (curr_loc_fips < 10){
curr_loc_fips = paste0(0,curr_loc_fips)
}else{curr_loc_fips = as.character(curr_loc_fips)}
# condition = expression((location==curr_loc_fips))
all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition,tar_type)
all_props <- c(all_props,curr_prop)
}
calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)
figfilename_calibration <- sprintf("results_figures/US_%swkaheaddeaths_calibration.pdf",i)
pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop))+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle(title_text)
print(a3)
dev.off()
}
for (i in 1:4){
cond_target = sprintf("%s wk ahead cum death",i)
condition = expression((target==cond_target)&(location!="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("State-level - %s week ahead cumulative deaths",i)
all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition,tar_type)
all_props <- c(all_props,curr_prop)
}
calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)
figfilename_calibration <- sprintf("results_figures/state-level_%swkaheaddeaths_calibration.pdf",i)
pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop))+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle(title_text)
print(a3)
dev.off()
}
calib_df <- data.frame()
for (i in 1:4){
cond_target = sprintf("%s wk ahead cum death",i)
condition1 = expression((target==cond_target)&(location!="US"))
condition2 = expression((target==cond_target)&(location=="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("%s week ahead",i)
all_props1 <- c()
all_props2 <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop1 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition1,tar_type)
all_props1 <- c(all_props1,curr_prop1)
curr_prop2 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition2,tar_type)
all_props2 <- c(all_props2,curr_prop2)
}
temp1 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props1,level="state",target=title_text)
temp2 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props2,level="US",target=title_text)
calib_df <- rbind(calib_df,temp1,temp2)
}
figfilename_calibration <- sprintf("results_figures/deaths_calibration.pdf",i)
pdf(figfilename_calibration, width = 7, height = 4)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop,shape=level,color=level),cex=1.5)+
# geom_point(mapping=aes(x=exp_prop,y=us_prop,shape="b"),shape=19)+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle("PI Capture Rates for Cumulative Death Forecasts")+
facet_wrap(facets=vars(target),nrow=2)
print(a3)
dev.off()
null device
1
print(a3)

calib_df <- data.frame()
for (i in 1:4){
cond_target = sprintf("%s wk ahead cum case",i)
condition1 = expression((target==cond_target)&(location!="US"))
condition2 = expression((target==cond_target)&(location=="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("%s week ahead",i)
all_props1 <- c()
all_props2 <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
for (j in 1:length(all_CI_widths)){
curr_a_level = round(1 - all_CI_widths[j],3)
# curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
# expression((location == curr_loc_fips)),"case")
curr_prop1 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition1,tar_type)
all_props1 <- c(all_props1,curr_prop1)
curr_prop2 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
condition2,tar_type)
all_props2 <- c(all_props2,curr_prop2)
}
temp1 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props1,level="state",target=title_text)
temp2 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props2,level="US",target=title_text)
calib_df <- rbind(calib_df,temp1,temp2)
}
figfilename_calibration <- sprintf("results_figures/cases_calibration.pdf",i)
pdf(figfilename_calibration, width = 7, height = 4)
a3 <- ggplot(data=calib_df)+
geom_point(mapping=aes(x=exp_prop,y=act_prop,shape=level,color=level),cex=1.5)+
# geom_point(mapping=aes(x=exp_prop,y=us_prop,shape="b"),shape=19)+
geom_abline(slope=1,intercept=0)+
xlim(c(0,1))+ylim(c(0,1))+
xlab("Expected Proportion")+
ylab("Actual Proportion")+
ggtitle("PI Capture Rates for Cumulative Case Forecasts")+
facet_wrap(facets=vars(target),nrow=2)
print(a3)
dev.off()
null device
1
print(a3)

LS0tCnRpdGxlOiAiRXBpQ292REEgRmlndXJlcyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogCiAgICBrZWVwX3RleDogdHJ1ZQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpDcmVhdGVkIDIvMjIvMjEgXCwgSFJCCgpVcGRhdGVkIDMvOS8yMSBcLCBTd2l0Y2hlZCBDT1ZJRCBUcmFja2luZyBQcm9qZWN0IFRydXRoIHRvIGRvd25sb2FkIGZyb20gMTEvMTYvMjAyMAoKVXBkYXRlZCA0LzExLzIxIFwsIFVzZSByZXN1bHRzIHdoaWNoIGluY2x1ZGUgUFIKCgojIyBSZWFkIGluIGZvcmVjYXN0cyBhbmQgYXR0YWNoIHRydXRoIAoKYGBge3Igc2V0dXAsIGVjaG89RkFMU0Usd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQoKIyBnZXR3ZCgpCgpsaWJyYXJ5KCJjZGxUb29scyIscXVpZXRseSA9IFRSVUUpICMgcGFja2FnZSB0byBjb252ZXJ0IGZpcHMgdG8gc3RhdGUgYWJicmV2aWF0aW9ucwpsaWJyYXJ5KGdnbmV3c2NhbGUscXVpZXRseSA9IFRSVUUpCmxpYnJhcnkodGlkeXZlcnNlLHF1aWV0bHkgPSBUUlVFLHdhcm4uY29uZmxpY3RzPUZBTFNFKQpsaWJyYXJ5KGNvdmlkY2FzdCxxdWlldGx5ID0gVFJVRSkKbGlicmFyeShNTVdSd2VlayxxdWlldGx5ID0gVFJVRSkKbGlicmFyeShrbml0ciwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MixxdWlldGx5ID0gVFJVRSkKCgojIEVwaUNvdkRBIEZvcmVjYXN0cyAtIENUUApmb3JlY2FzdHNfd2l0aF90cnV0aCA9IHJlYWQuY3N2KCJVQS1FcGlDb3ZEQS12My1hbHBoYURhdGEtdHViZTEtcHJpb3ItMjAyMC0xMS0xNi1QQVBFUi9mb3JlY2FzdHNfd2l0aF90cnV0aC1VQS1FcGlDb3ZEQS1DVFAuY3N2IikKRXBpQ292REFfcHRfc2NvcmVzID0gcmVhZC5jc3YoIlVBLUVwaUNvdkRBLXYzLWFscGhhRGF0YS10dWJlMS1wcmlvci0yMDIwLTExLTE2LVBBUEVSL0NUUF9hbGxfc2NvcmVzLmNzdiIpCgpFcGlDb3ZEQV9wdF9zY29yZXMgPSBzdWJzZXQoRXBpQ292REFfcHRfc2NvcmVzLGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSk+PSBhcy5EYXRlKCIyMDIwLTA1LTAxIikpCmZvcmVjYXN0c193aXRoX3RydXRoID0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSk+PSBhcy5EYXRlKCIyMDIwLTA1LTAxIikpCgoKYGBgCgoKYGBge3J9Cm15ZmlwczwtZnVuY3Rpb24oZmlwc19jb2Rlcyx0bz0iQWJicmV2aWF0aW9uIil7CiAgZmlwc19hYmJyZXYgPC0gZmlwc19jb2RlcwogIGZpcHNfYWJicmV2W2ZpcHNfY29kZXMhPSJVUyJdIDwtIGZpcHMoZmlwc19jb2Rlc1tmaXBzX2NvZGVzIT0iVVMiXSx0bz0iQWJicmV2aWF0aW9uIikgCiAgZmlwc19hYmJyZXZbZmlwc19jb2Rlcz09IlVTIl0gPC0gIiBVUyIKICAKICByZXR1cm4oZmlwc19hYmJyZXYpCiAgCn0KYGBgCgoKCiMjIENhbGN1bGF0ZSBpbnRlcnZhbCBzY29yZXMgZm9yIGEgc3BlY2lmaWVkIGxldmVsIChhbHBoYSkKCiQkIFx0ZXh0e0lTfV97XGFscGhhfShGLHkpID0gKHUgLSBcZWxsKSArIFxmcmFjezJ9e1xhbHBoYX0gKFxlbGwgLSB5KSBcbWF0aGJmezF9KFxlbGwgPiB5KSArIFxmcmFjezJ9e1xhbHBoYX0oeSAtIHUpXG1hdGhiZnsxfSh1IDwgeSkkJAoKCmBgYHtyIGRlZmluZS1nZXRJbnRlcnZhbFNjb3Jlcy1mdW5jdGlvbiwgZWNobz1GQUxTRX0KZ2V0SW50ZXJ2YWxTY29yZXMgPC0gZnVuY3Rpb24oZm9yZWNhc3RzX3dpdGhfdHJ1dGgsYWxwaGFfbGV2ZWwpewoKICB1cHBlcmJvdW5kcyA9IHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PSgxLWFscGhhX2xldmVsLzIpLAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD0idmFsdWUiICkKICAKICBsb3dlcmJvdW5kcyA9IHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PShhbHBoYV9sZXZlbC8yKSwKICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Q9InZhbHVlIiApCiAgCiAgdHJ1dGhzID0gIHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PShhbHBoYV9sZXZlbC8yKSwKICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Q9InRydXRoIiApCiAgCiAgaW50ZXJ2YWxfc2NvcmVzID0gKHVwcGVyYm91bmRzLWxvd2VyYm91bmRzKSArIAogICAgICAgICAgICAgICAgICAgIDIvYWxwaGFfbGV2ZWwgKiAobG93ZXJib3VuZHMtdHJ1dGhzKSoKICAgICAgICAgICAgICAgICAgICAgICAgICAobG93ZXJib3VuZHM+dHJ1dGhzKSArIAogICAgICAgICAgICAgICAgICAgIDIvYWxwaGFfbGV2ZWwgKiAodHJ1dGhzLXVwcGVyYm91bmRzKSoodXBwZXJib3VuZHM8dHJ1dGhzKQogIAogIElTX2RmID0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLHF1YW50aWxlID09KGFscGhhX2xldmVsLzIpLAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD1jKCJmb3JlY2FzdF9kYXRlIiwidGFyZ2V0IiwidGFyZ2V0X2VuZF9kYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRpb24iLCJ0cnV0aCIpICkKICAKICBJU19kZiRpbnRlcnZhbF9zY29yZSA9IGludGVydmFsX3Njb3JlcyR2YWx1ZQogIAogIHJldHVybihJU19kZikKfQpgYGAKCgoKCiMjIENhbGN1bGF0ZSBXZWlnaHRlZCBJbnRlcnZhbCBTY29yZSAoV0lTKQoKJCQgXHRleHR7V0lTfV97XGFscGhhXzAgOiBLfShGLHkpID0gXGZyYWN7MX17SysxLzJ9XGxlZnQod18wIFwsIHx5LW18ICsgXHN1bV97az0xfV5LIHdfayBcLCBcdGV4dHtJU31fe1xhbHBoYV9rfShGLHkpXHJpZ2h0KSAkJAp3aGVyZSAkd18wID0gXGZyYWMgMSAyJCBhbmQgJHdfayA9IFxmcmFje1xhbHBoYV9rfXsyfSQgZm9yICRrID0gMSxcaGRvdHMsIEskIGZvciAkXGFscGhhPSAwLjAyLCAwLjA1LCAwLjEsIDAuMiwgXGhkb3RzLCAwLjkkLiAgCgoKCmBgYHtyIGRlZmluZS1nZXRXSVMtZnVuY3Rpb259CmdldFdJUyA8LSBmdW5jdGlvbihmb3JlY2FzdHNfd2l0aF90cnV0aCl7CgoKICAKICB3MCA9IDAuNQogIGZfbWVkaWFucyA9ICBzdWJzZXQoZm9yZWNhc3RzX3dpdGhfdHJ1dGgscXVhbnRpbGUgPT0wLjUsCiAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0PSJ2YWx1ZSIgKQogIHRydXRoX3kgPSBzdWJzZXQoZm9yZWNhc3RzX3dpdGhfdHJ1dGgscXVhbnRpbGUgPT0wLjUsCiAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0PSJ0cnV0aCIgKQogIFdJUyA8LSB3MCphYnModHJ1dGhfeS1mX21lZGlhbnMpCiAgCiAgZm9yIChhbHBoYV9sZXZlbCBpbiBjKDAuMDIsMC4wNSwwLjEsMC4yLDAuMywwLjQsMC41LDAuNiwwLjcsMC44LDAuOSkpewogICAgd2sgPSBhbHBoYV9sZXZlbC8yCiAgICB0ZW1wX0lTIDwtIGdldEludGVydmFsU2NvcmVzKGZvcmVjYXN0c193aXRoX3RydXRoLGFscGhhX2xldmVsKQogICAgCiAgICBXSVMgPC0gV0lTICsgd2sqdGVtcF9JUyRpbnRlcnZhbF9zY29yZQogICAgCiAgfQogIAogICAgV0lTX3RlbXBfZGYgPC0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLHF1YW50aWxlID09MC41LAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD1jKCJmb3JlY2FzdF9kYXRlIiwidGFyZ2V0IiwidGFyZ2V0X2VuZF9kYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRpb24iLCJ0cnV0aCIpICkKICAgIFdJU190ZW1wX2RmJFdJUyA8LSBXSVMkdHJ1dGgKICAKICAKIAogIAogIHJldHVybihXSVNfdGVtcF9kZikKfQpgYGAKCgoKCmBgYHtyIGNhbGN1bGF0ZS1tb2RlbC1XSVN9CkVwaUNvdkRBX1dJUyA8LSBnZXRXSVMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgpCgpFcGlDb3ZEQV9XSVMkSVNfMDUgPC0gZ2V0SW50ZXJ2YWxTY29yZXMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsIDAuMDUpJGludGVydmFsX3Njb3JlCgpFcGlDb3ZEQV9XSVMkSVNfNTAgPC0gZ2V0SW50ZXJ2YWxTY29yZXMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsIDAuNSkkaW50ZXJ2YWxfc2NvcmUKCmBgYAoKCiMgQXR0YWNoIHN0YXRlIHBvcHVsYXRpb24KCgpgYGB7cn0KRXBpQ292REFfV0lTJHBvcHVsYXRpb24gPC0gTkEKRXBpQ292REFfcHRfc2NvcmVzJHBvcHVsYXRpb24gPC0gTkEKCgoKZm9yIChsb2MgaW4gdW5pcXVlKEVwaUNvdkRBX1dJUyRsb2NhdGlvbikpewogIGlmIChsb2MgPT0gIlVTIil7CiAgICB0ZW1wX3BvcCA8LSBhcy5udW1lcmljKHJlYWQuY3N2KCIuLi9zdGF0ZV9ob3NwX2RhdGFfMjAyMC0xMS0xNi9zdGF0ZV9wb3BVUy5jc3YiKSkKICAgIEVwaUNvdkRBX1dJU1t3aGljaChFcGlDb3ZEQV9XSVMkbG9jYXRpb249PSJVUyIpLCJwb3B1bGF0aW9uIl0gPC0gdGVtcF9wb3AKICAgIEVwaUNvdkRBX3B0X3Njb3Jlc1t3aGljaChFcGlDb3ZEQV9wdF9zY29yZXMkbG9jYXRpb24gPT0gIlVTIiksInBvcHVsYXRpb24iXSA8LSB0ZW1wX3BvcAoKICAgIAogIH0gZWxzZSB7CgogICAgICAgIHRlbXBfcG9wIDwtIGFzLm51bWVyaWMocmVhZC5jc3Yoc3ByaW50ZigiLi4vc3RhdGVfaG9zcF9kYXRhXzIwMjAtMTEtMTYvc3RhdGVfcG9wJXMuY3N2IixmaXBzKGxvYywgdG8gPSAiQWJicmV2aWF0aW9uIikpKSkKICAgIEVwaUNvdkRBX1dJU1t3aGljaChFcGlDb3ZEQV9XSVMkbG9jYXRpb249PWxvYyksInBvcHVsYXRpb24iXSA8LSB0ZW1wX3BvcAogICAgRXBpQ292REFfcHRfc2NvcmVzW3doaWNoKEVwaUNvdkRBX3B0X3Njb3JlcyRsb2NhdGlvbiA9PSBmaXBzKGxvYywgdG8gPSAiQWJicmV2aWF0aW9uIikpLCJwb3B1bGF0aW9uIl0gPC0gdGVtcF9wb3AKCiAgCiAgfQogIAogIAp9CgoKCgpgYGAKCgoKCmBgYHtyIGRlZmluZS1zdGF0c09uUGVyZm9ybWFuY2UtZnVuY3Rpb259CnN0YXRzT25QZXJmb3JtYW5jZSA8LSBmdW5jdGlvbihhbGxfSVMsY29sdW1uLHRhcl90eXBlID0gImRlYXRoIixmaWx0ZXJkYXRlc19URiA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlVVMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVTb25seSA9IEZBTFNFKXsKICAKICBhbGxfdGFyZ2V0cyA8LSBjKCIxIHdrIGFoZWFkIGN1bSBkZWF0aCIsIjIgd2sgYWhlYWQgY3VtIGRlYXRoIiwiMyB3ayBhaGVhZCBjdW0gZGVhdGgiLAogICAgICAgICAgICAgICAgICAgIjQgd2sgYWhlYWQgY3VtIGRlYXRoIikKICAKICBpZiAodGFyX3R5cGUgPT0gImNhc2UiKXsKICAgIGFsbF90YXJnZXRzIDwtIGMoIjEgd2sgYWhlYWQgY3VtIGNhc2UiLCIyIHdrIGFoZWFkIGN1bSBjYXNlIiwKICAgICAgICAgICAgICAgICAgICAgIjMgd2sgYWhlYWQgY3VtIGNhc2UiLAogICAgICAgICAgICAgICAgICAgICAiNCB3ayBhaGVhZCBjdW0gY2FzZSIpCiAgfQogIAogIAogIAogIGlmIChmaWx0ZXJkYXRlc19URil7CiAgICBhbGxfSVMgPC0gc3Vic2V0KGFsbF9JUywoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKT5hcy5EYXRlKCIyMDIwLTA0LTE5IikpKQogICAgCiAgfQogIAogIGlmICghaW5jbHVkZVVTKXsKICAgIHN1YnNldChhbGxfSVMsKGxvY2F0aW9uIT0iVVMiKSkKICB9CiAgCiAgaWYgKFVTb25seSl7CiAgICBhbGxfSVMgPC0gc3Vic2V0KGFsbF9JUywobG9jYXRpb249PSJVUyIpKQogIH0KCgogIAogIHN0YXRzX2RmIDwtIGRhdGEuZnJhbWUoKQogIAogIHRlbXBfZGYgPC0gZGF0YS5mcmFtZSh0YXJnZXQgPSAiT3ZlcmFsbCIsbWVhbiA9IG1lYW4oYWxsX0lTW2dyZXBsKHRhcl90eXBlLGFsbF9JUyR0YXJnZXQpLGNvbHVtbl0pLAogICAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4oYWxsX0lTW2dyZXBsKHRhcl90eXBlLGFsbF9JUyR0YXJnZXQpLGNvbHVtbl0pKQogIAogIHN0YXRzX2RmIDwtIHJiaW5kKHN0YXRzX2RmLHRlbXBfZGYpCiAgCiAgCiAgCiAgZm9yICh0YXIgaW4gYWxsX3RhcmdldHMpewogICAgY3Vycl9zdWJzZXQgPC0gc3Vic2V0KGFsbF9JUyx0YXJnZXQgPT0gdGFyKQogICAgY3Vycl9tZWFuIDwtIG1lYW4oY3Vycl9zdWJzZXRbLGNvbHVtbl0pCiAgICBjdXJyX21lZGlhbiA8LSBtZWRpYW4oY3Vycl9zdWJzZXRbLGNvbHVtbl0pCiAgICB0ZW1wX2RmIDwtIGRhdGEuZnJhbWUodGFyZ2V0ID0gdGFyLCBtZWFuID0gY3Vycl9tZWFuLG1lZGlhbiA9IGN1cnJfbWVkaWFuKQogICAgCiAgICBzdGF0c19kZiA8LSByYmluZChzdGF0c19kZix0ZW1wX2RmKQogIH0KICAKICByZXR1cm4oc3RhdHNfZGYpCn0KYGBgCgoKCgoKYGBge3J9CklTX3N0YXRzX0VwaUNvdkRBIDwtIHN0YXRzT25QZXJmb3JtYW5jZShFcGlDb3ZEQV9XSVMsIldJUyIpCmBgYAoKCgpgYGB7cn0KIyBrYWJsZShJU19zdGF0c19FcGlDb3ZEQSxib29rdGFicz1UUlVFKQojIGthYmxlKElTX3N0YXRzX0NPVklEaHViLGJvb2t0YWJscz1UUlVFKQpgYGAKCgoKIyBQb2ludCBTY29yZXMKCgpgYGB7cn0KRXBpQ292REFfcHRfc2NvcmVzJGFic19lcnJvciA8LSBhYnMoRXBpQ292REFfcHRfc2NvcmVzJGVycm9yKQoKRXBpQ292REFfcHRfc3RhdHMgPC0gc3RhdHNPblBlcmZvcm1hbmNlKEVwaUNvdkRBX3B0X3Njb3JlcywiYWJzX2Vycm9yIikKCmBgYAoKCiMgUGxvdCBJU19hbHBoYQoKYGBge3J9CgoKCmdncGxvdCgpKwogIGdlb21fbGluZShkYXRhPUVwaUNvdkRBX1dJU1soRXBpQ292REFfV0lTJHRhcmdldD09IjEgd2sgYWhlYWQgY3VtIGRlYXRoIikmKEVwaUNvdkRBX1dJUyRsb2NhdGlvbj09IlVTIiksXSwKICAgICAgICAgICAgbWFwcGluZz1hZXMoeD1hcy5EYXRlKGZvcmVjYXN0X2RhdGUpLHk9SVNfMDUsZ3JvdXA9bG9jYXRpb24pKQoKCgpnZ3Bsb3QoKSsKICAgIGdlb21fbGluZShkYXRhPXN1YnNldCgKICAgICAgICAgICAgICAgIEVwaUNvdkRBX3B0X3Njb3Jlc1tncmVwbCgiZGVhdGgiLEVwaUNvdkRBX3B0X3Njb3JlcyR0YXJnZXQpLF0sbG9jYXRpb249PSJVUyIpLAogICAgICAgICAgICBtYXBwaW5nPWFlcyh4PWFzLkRhdGUoZm9yZWNhc3RfZGF0ZSkseT1hYnNfZXJyb3IsZ3JvdXA9dGFyZ2V0LGNvbG9yPXRhcmdldCkpCgoKYGBgCgoKYGBge3J9CklTX2RlYXRoc19kZiA8LSBFcGlDb3ZEQV9XSVNbZ3JlcGwoImRlYXRoIixFcGlDb3ZEQV9XSVMkdGFyZ2V0KSxdCgoKdGhpc2ZpbGVuYW1lIDwtICJyZXN1bHRzX2ZpZ3VyZXMvVVNfSVMucGRmIgoKcGRmKHRoaXNmaWxlbmFtZSwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNikKYTMgPC0gZ2dwbG90KCkrCiAgZ2VvbV9saW5lKGRhdGE9SVNfZGVhdGhzX2RmWyhJU19kZWF0aHNfZGYkbG9jYXRpb249PSJVUyIpLF0sCiAgICAgICAgICAgIG1hcHBpbmc9YWVzKHg9YXMuRGF0ZShmb3JlY2FzdF9kYXRlKSx5PUlTXzA1LGdyb3VwPXRhcmdldCxjb2xvcj10YXJnZXQpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiSW50ZXJ2YWwgU2NvcmUiKStnZ3RpdGxlKCJJbnRlcnZhbCBTY29yZXMgZm9yIFVTIERlYXRoIEZvcmVjYXN0cyIpKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZT0iVGFyZ2V0IixsYWJlbHMgPSBjKCIxIHdrIGFoZWFkIGNtbHR2IGRlYXRocyIsIjIgd2sgYWhlYWQgY21sdHYgZGVhdGhzIiwiMyB3ayBhaGVhZCBjbWx0diBkZWF0aHMiLCI0IHdrIGFoZWFkIGNtbHR2IGRlYXRocyIpKSsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCnByaW50KGEzKQpkZXYub2ZmKCkKCmBgYAoKCiMgRGVhdGggUmVzdWx0czogV2VlayBhaGVhZCBpbnRlcnZhbCBzY29yZSBmaWd1cmVzCgoKUGVyIDEwMCwwMDAgcG9wdWxhdGlvbgoKTWF4IElTXzA1L3BvcHVsYXRpb24qMTBeNSBpcyBhYm91dCA2NzcKCmBgYHtyIGRlYXRocy1JU30KCiMgZm9yIChpIGluIDE6NCl7CgoKbXlicmVha3MgPSBjKDAsMSw1LDEwLDI1LDUwLDEwMCw1MDAsSW5mKQpteWxhYmVscyA9IGMoIjwxIiwiMSA8PSB4IDwgNSIsICI1IDw9IHggPCAxMCIsICIxMCA8PSB4IDwgMjUiLCIyNSA8PSB4IDwgNTAiLCI1MCA8PSB4IDwgMTAwIiwgIjEwMCA8PSB4IDwgNTAwIiwiPj01MDAiKQoKZmlnZmlsZW5hbWUzIDwtIHNwcmludGYoInJlc3VsdHNfZmlndXJlcy9JU19lcGljb3ZkYV9kZWF0aHMucGRmIikKCnBkZihmaWdmaWxlbmFtZTMsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpCnAxIDwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gSVNfZGVhdGhzX2RmLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPWN1dChJU18wNS9wb3B1bGF0aW9uKjEwXjUsYnJlYWtzPW15YnJlYWtzLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVscykpKSArCiAgIyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLGhpZ2g9ImZpcmVicmljazMiLG1pZD0iZ29sZCIsdHJhbnM9ImxvZzEwIixtaWRwb2ludD0xLAogICMgICAgICAgICAgICAgICAgICAgICBicmVha3M9bXlicmVha3MsbGFiZWxzPW15YnJlYWtzKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKSsKICBnZ3RpdGxlKCJFcGlDb3ZEQSBJbnRlcnZhbCBTY29yZXMgKGFscGhhPTAuMDUpIGZvciBEZWF0aCBGb3JlY2FzdHMiKSsKICAjIGdndGl0bGUoc3ByaW50ZigiRXBpQ292REEsICVzIHdrIGFoZWFkIGN1bXVsYXRpdmUgZGVhdGhzIixpKSkrCiAgbGFicyhmaWxsPSJJUyBwZXIgMTAwLDAwMCIpKwogIGZhY2V0X3dyYXAodmFycyh0YXJnZXQpLHNjYWxlcz0iZml4ZWQiLG5yb3c9MSkKcHJpbnQocDEpCmRldi5vZmYoKQpwcmludChwMSkKCiMgfQoKIyBtYXgoSVNfZGVhdGhzX2RmJElTXzA1L0lTX2RlYXRoc19kZiRwb3B1bGF0aW9uKjEwXjUpCgoKYGBgCgoKIyBQb2ludCBFc3RpbWF0ZSBFcnJvcnMKCk1heCBjdW11bGF0aXZlIGRlYXRocyBmb3IgNCB3ZWVrIGFoZWFkIGZvcmVjYXN0czogYWJvdXQgMjguOSBwZXIgMTAwLDAwMAoKYGBge3IgQUUtZGVhdGhzMX0KCm15YnJlYWtzMiA8LSBjKDAsLjUsMSw1LDEwLDE1LDI1LEluZikKbXlsYWJlbHMyID0gYygiPDAuNSIsIjAuNSA8PSB4IDwgMSIsIjEgPD0geCA8IDUiLCAiNSA8PSB4IDwgMTAiLCAiMTAgPD0geCA8IDE1IiwiMTUgPD0geCA8IDI1IiwiPj0yNSIpCgoKCmZpZ2ZpbGVuYW1lMSA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvQUVfZXBpY292ZGFfZGVhdGhzLnBkZiIpCgoKCgpwZGYoZmlnZmlsZW5hbWUxLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQpwMzwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfcHRfc2NvcmVzW2dyZXBsKCJkZWF0aCIsRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldCksXSwKICAgICAgICAgICAgbWFwcGluZyA9CiAgICAgICAgICAgICAgYWVzKGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSksCiAgICAgICAgICAgICAgICAgIG15Zmlwcyhhcy5jaGFyYWN0ZXIobG9jYXRpb24pLHRvPSJBYmJyZXZpYXRpb24iKSwKICAgICAgICAgICAgICAgICAgZmlsbD0gICBjdXQoYWJzX2Vycm9yL3BvcHVsYXRpb24qMTBeNSxicmVha3M9bXlicmVha3MyLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVsczIpICkpICsKICAjIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJ3aGl0ZSIsbWlkPSJnb2xkIixoaWdoPSJmaXJlYnJpY2szIiwKICAjICAgICAgICAgICAgICAgICAgICAgIHRyYW5zPSJsb2cxMCIsbWlkcG9pbnQgPSBsb2cxMCguNSksbmEudmFsdWUgPSAid2hpdGUiLGJyZWFrcz1teWJyZWFrczIsCiAgIyAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9bXlicmVha3MyKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKStnZ3RpdGxlKCJFcGlDb3ZEQSBBYnNvbHV0ZSBFcnJvciBwZXIgMTAwLDAwMCBmb3IgRGVhdGggRm9yZWNhc3RzIikgKwogIGxhYnMoZmlsbD0iQWJzLiBFcnJvciBwZXIgMTAwLDAwMCIpKwogIGZhY2V0X3dyYXAodmFycyh0YXJnZXQpLHNjYWxlcz0iZml4ZWQiLG5yb3c9MSkKcHJpbnQocDMpCmRldi5vZmYoKQpwcmludChwMykKCgoKCiMgbWF4KEVwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGRlYXRoIixqKSksImFic19lcnJvciJdL0VwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGRlYXRoIixqKSksInBvcHVsYXRpb24iXSoxMF41KQpgYGAKCgoKIyMgIkhlYXRtYXBzIiBmb3IgQ2FzZXMgCgptYXggSVMgZm9yIGNhc2VzIGFib3V0IDIzNzUyCm1lZGlhbnQgSVMgZm9yIGNhc2VzIGFib3V0IDE5MwoKYGBge3IgY2FzZXMtSVN9CgojIGZvciAoaSBpbiAxOjQpewoKCm15YnJlYWtzID0gYygwLDUsMjUsMTAwLDMwMCwxMDAwLDUwMDAsMTAwMDAsSW5mKQpteWxhYmVscyA9IGMoIjw1IiwiNSA8PSB4IDwgMjUiLCAiMjUgPD0geCA8IDEwMCIsICIxMDAgPD0geCA8IDMwMCIsIjMwMCA8PSB4IDwgMTAwMCIsIjEwMDAgPD0geCA8IDUwMDAiLCAiNTAwMCA8PSB4IDwgMTAwMDAiLCI+PTEwMDAwIikKCmZpZ2ZpbGVuYW1lMyA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvSVNfZXBpY292ZGFfY2FzZXMucGRmIikKCnBkZihmaWdmaWxlbmFtZTMsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpCnAxIDwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfV0lTW2dyZXBsKCJjYXNlIixFcGlDb3ZEQV9XSVMkdGFyZ2V0KSxdLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPWN1dChJU18wNS9wb3B1bGF0aW9uKjEwXjUsYnJlYWtzPW15YnJlYWtzLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVscykpKSArCiAgIyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLGhpZ2g9ImZpcmVicmljazMiLG1pZD0iZ29sZCIsdHJhbnM9ImxvZzEwIixtaWRwb2ludD0xLAogICMgICAgICAgICAgICAgICAgICAgICBicmVha3M9bXlicmVha3MsbGFiZWxzPW15YnJlYWtzKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKSsKICBnZ3RpdGxlKCJFcGlDb3ZEQSBJbnRlcnZhbCBTY29yZXMgKGFscGhhPTAuMDUpIGZvciBDYXNlIEZvcmVjYXN0cyIpKwogICMgZ2d0aXRsZShzcHJpbnRmKCJFcGlDb3ZEQSwgJXMgd2sgYWhlYWQgY3VtdWxhdGl2ZSBkZWF0aHMiLGkpKSsKICBsYWJzKGZpbGw9IklTIHBlciAxMDAsMDAwIikrCiAgZmFjZXRfd3JhcCh2YXJzKHRhcmdldCksc2NhbGVzPSJmaXhlZCIsbnJvdz0xKQpwcmludChwMSkKZGV2Lm9mZigpCnByaW50KHAxKQoKIyB9CgojIG1lZGlhbihFcGlDb3ZEQV9XSVNbZ3JlcGwoImNhc2UiLEVwaUNvdkRBX1dJUyR0YXJnZXQpLCJJU18wNSJdL0VwaUNvdkRBX1dJU1tncmVwbCgiY2FzZSIsRXBpQ292REFfV0lTJHRhcmdldCksInBvcHVsYXRpb24iXSoxMF41KQoKCmBgYAoKCiMgUG9pbnQgRXN0aW1hdGUgRXJyb3JzCgpNYXggY3VtdWxhdGl2ZSBkZWF0aHMgZm9yIDQgd2VlayBhaGVhZCBmb3JlY2FzdHM6IGFib3V0IDg2NyBwZXIgMTAwLDAwMAptZWRpYW4gYWJvdXQgNjcKCmBgYHtyIEFFLWRlYXRoc30KCm15YnJlYWtzMiA8LSBjKDAsMSwxMCwyNSw1MCwxMDAsMzAwLDUwMCxJbmYpCm15bGFiZWxzMiA9IGMoIjwxIiwiMSA8PSB4IDwgMTAiLCIxMCA8PSB4IDwgMjUiLCAiMjUgPD0geCA8IDUwIiwgIjUwIDw9IHggPCAxMDAiLCIxMDAgPD0geCA8IDMwMCIsCiAgICAgICAgICAgICAgIjMwMCA8PSB4IDwgNTAwIiwiPj01MDAiKQoKCgpmaWdmaWxlbmFtZTEgPC0gc3ByaW50ZigicmVzdWx0c19maWd1cmVzL0FFX2VwaWNvdmRhX2Nhc2VzLnBkZiIpCgoKCgpwZGYoZmlnZmlsZW5hbWUxLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQpwMzwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfcHRfc2NvcmVzW2dyZXBsKCJjYXNlIixFcGlDb3ZEQV9wdF9zY29yZXMkdGFyZ2V0KSxdLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPSAgIGN1dChhYnNfZXJyb3IvcG9wdWxhdGlvbioxMF41LGJyZWFrcz1teWJyZWFrczIscmlnaHQ9RkFMU0UsbGFiZWxzPW15bGFiZWxzMikgKSkgKwogICMgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gIndoaXRlIixtaWQ9ImdvbGQiLGhpZ2g9ImZpcmVicmljazMiLAogICMgICAgICAgICAgICAgICAgICAgICAgdHJhbnM9ImxvZzEwIixtaWRwb2ludCA9IGxvZzEwKC41KSxuYS52YWx1ZSA9ICJ3aGl0ZSIsYnJlYWtzPW15YnJlYWtzMiwKICAjICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1teWJyZWFrczIpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InNlcSIscGFsZXR0ZSA9ICJZbE9yUmQiKSsKICB0aGVtZV9idygpKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpKwogIHhsYWIoIkZvcmVjYXN0IERhdGUiKSt5bGFiKCJTdGF0ZSIpK2dndGl0bGUoIkVwaUNvdkRBIEFic29sdXRlIEVycm9yIHBlciAxMDAsMDAwIGZvciBDYXNlIEZvcmVjYXN0cyIpICsKICBsYWJzKGZpbGw9IkFicy4gRXJyb3IgcGVyIDEwMCwwMDAiKSsKICBmYWNldF93cmFwKHZhcnModGFyZ2V0KSxzY2FsZXM9ImZpeGVkIixucm93PTEpCnByaW50KHAzKQpkZXYub2ZmKCkKcHJpbnQocDMpCgoKCgojIG1lZGlhbihFcGlDb3ZEQV9wdF9zY29yZXNbKEVwaUNvdkRBX3B0X3Njb3JlcyR0YXJnZXQ9PXNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIiw0KSksImFic19lcnJvciJdL0VwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGNhc2UiLDQpKSwicG9wdWxhdGlvbiJdKjEwXjUpCgpgYGAKCgoKCgoKCmBgYHtyfQoKZm9yZWNhc3RlZF9sb2NhdGlvbnMgPSB1bmlxdWUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24pCgp0YXJfdHlwZSA9ICJjYXNlIgoKZm9yIChsb2MgaW4gZm9yZWNhc3RlZF9sb2NhdGlvbnMpewoKc3RhdGVfbnVtID0gbG9jICMiMDQiCgp0YXJnZXRfZm9yZWNhc3RzX3dpdGhfdHJ1dGggPC0KICBmb3JlY2FzdHNfd2l0aF90cnV0aFtncmVwbCh0YXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JlY2FzdHNfd2l0aF90cnV0aCR0YXJnZXQpICYKICAgICAgICAgICAgICAgICAgICAgICAgIChmb3JlY2FzdHNfd2l0aF90cnV0aCRsb2NhdGlvbiA9PSBzdGF0ZV9udW0pJgogICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShmb3JlY2FzdHNfd2l0aF90cnV0aCRmb3JlY2FzdF9kYXRlKQogICAgICAgICAgICAgICAgICAgICAgID5hcy5EYXRlKCcyMDIwLTA0LTEwJyksXQoKCnN0X2FiID0gZmlwcyhzdGF0ZV9udW0sdG89IkFiYnJldmlhdGlvbiIpCgppZiAobG9jID09ICJVUyIpewogIHN0X2FiID0gIlVTIgp9CmZjYXN0c193aWRlIDwtIGFzX3RpYmJsZSh0YXJnZXRfZm9yZWNhc3RzX3dpdGhfdHJ1dGgpICU+JQogICAgZmlsdGVyKHF1YW50aWxlICVpbiUgYygwLjAyNSwgMC4xLCAwLjI1LCAwLjUsIDAuNzUsIDAuOSwgMC45NzUpKSAlPiUKICAgIG11dGF0ZSh3ZWVrX2FoZWFkID0gYXMubnVtZXJpYyhzdWJzdHIodGFyZ2V0LCAwLDIpKSkgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcXVhbnRpbGUsIG5hbWVzX3ByZWZpeD0icSIpCgoKCmZpZ2ZpbGVuYW1lIDwtIHNwcmludGYoInJlc3VsdHNfZmlndXJlcy8lc18lc19xdWFudF9mb3JlY2FzdHMucGRmIixzdF9hYix0YXJfdHlwZSkKCnBkZihmaWdmaWxlbmFtZSwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMSA8LSBnZ3Bsb3QoZmNhc3RzX3dpZGUsYWVzKHggPSBhcy5EYXRlKHRhcmdldF9lbmRfZGF0ZSkpKSsKICAgIGdlb21fbGluZShhZXMoeT1xMC41LCBjb2xvcj1mb3JlY2FzdF9kYXRlLCBncm91cD1mb3JlY2FzdF9kYXRlKSkgKwogICAgIyBnZW9tX3JpYmJvbihhZXMoeW1pbj1xMC4xLCB5bWF4PXEwLjksIGZpbGw9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSksIGFscGhhPS4zKSArCiAgICBnZW9tX3JpYmJvbihhZXMoeW1pbj1xMC4wMjUsIHltYXg9cTAuOTc1LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMjUsIHltYXg9cTAuNzUsIGZpbGw9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSksIGFscGhhPS4zKSArCiAgICBnZW9tX3BvaW50KGFlcyh5PXRydXRoKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PXRydXRoKSkgKwogICAgdGhlbWVfYncoKSArIHhsYWIoIkRhdGUiKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgeWxhYihzcHJpbnRmKCJjdW11bGF0aXZlICVzIixwYXN0ZTAodGFyX3R5cGUsInMiKSkpICsKICAgIGdndGl0bGUoc3ByaW50ZigiQ3VtdWxhdGl2ZSAlcyBpbiAlcywgb2JzZXJ2ZWQgYW5kIGZvcmVjYXN0ZWQiLHBhc3RlMCh0YXJfdHlwZSwicyIpLHN0X2FiKSkKIysKICAgICMgY29vcmRfY2FydGVzaWFuKHlsaW09YygyNTAsIDI1MDApKQoKcHJpbnQoYTEpCmRldi5vZmYoKQpwcmludChhMSkKCgoKfQpgYGAKCgoKCmBgYHtyfQoKZm9yZWNhc3RlZF9sb2NhdGlvbnMgPSB1bmlxdWUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24pCgp0YXJfdHlwZSA9ICJkZWF0aCIKCmZvciAobG9jIGluIGZvcmVjYXN0ZWRfbG9jYXRpb25zKXsKCnN0YXRlX251bSA9IGxvYyAjIjA0IgoKdGFyZ2V0X2ZvcmVjYXN0c193aXRoX3RydXRoIDwtCiAgZm9yZWNhc3RzX3dpdGhfdHJ1dGhbZ3JlcGwodGFyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yZWNhc3RzX3dpdGhfdHJ1dGgkdGFyZ2V0KSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24gPT0gc3RhdGVfbnVtKSYKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkZm9yZWNhc3RfZGF0ZSkKICAgICAgICAgICAgICAgICAgICAgICA+YXMuRGF0ZSgnMjAyMC0wNC0xMCcpLF0KCgpzdF9hYiA9IGZpcHMoc3RhdGVfbnVtLHRvPSJBYmJyZXZpYXRpb24iKQoKaWYgKGxvYyA9PSAiVVMiKXsKICBzdF9hYiA9ICJVUyIKfQpmY2FzdHNfd2lkZSA8LSBhc190aWJibGUodGFyZ2V0X2ZvcmVjYXN0c193aXRoX3RydXRoKSAlPiUKICAgIGZpbHRlcihxdWFudGlsZSAlaW4lIGMoMC4wMjUsIDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjksIDAuOTc1KSkgJT4lCiAgICBtdXRhdGUod2Vla19haGVhZCA9IGFzLm51bWVyaWMoc3Vic3RyKHRhcmdldCwgMCwyKSkpICU+JQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHF1YW50aWxlLCBuYW1lc19wcmVmaXg9InEiKQoKCgpmaWdmaWxlbmFtZSA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvJXNfJXNfcXVhbnRfZm9yZWNhc3RzLnBkZiIsc3RfYWIsdGFyX3R5cGUpCgpwZGYoZmlnZmlsZW5hbWUsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKYTEgPC0gZ2dwbG90KGZjYXN0c193aWRlLGFlcyh4ID0gYXMuRGF0ZSh0YXJnZXRfZW5kX2RhdGUpKSkrCiAgICBnZW9tX2xpbmUoYWVzKHk9cTAuNSwgY29sb3I9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSkpICsKICAgICMgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMSwgeW1heD1xMC45LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMDI1LCB5bWF4PXEwLjk3NSwgZmlsbD1mb3JlY2FzdF9kYXRlLCBncm91cD1mb3JlY2FzdF9kYXRlKSwgYWxwaGE9LjMpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPXEwLjI1LCB5bWF4PXEwLjc1LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9wb2ludChhZXMoeT10cnV0aCkpICsKICAgIGdlb21fbGluZShhZXMoeT10cnV0aCkpICsKICAgIHRoZW1lX2J3KCkgKyB4bGFiKCJEYXRlIikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIHlsYWIoc3ByaW50ZigiY3VtdWxhdGl2ZSAlcyIscGFzdGUwKHRhcl90eXBlLCJzIikpKSArCiAgICBnZ3RpdGxlKHNwcmludGYoIkN1bXVsYXRpdmUgJXMgaW4gJXMsIG9ic2VydmVkIGFuZCBmb3JlY2FzdGVkIixwYXN0ZTAodGFyX3R5cGUsInMiKSxzdF9hYikpCiMrCiAgICAjIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMjUwLCAyNTAwKSkKCnByaW50KGExKQpkZXYub2ZmKCkKcHJpbnQoYTEpCgoKCn0KYGBgCgoKCgoKCgpgYGB7cn0KCmdldF9jYWxpYnJhdGlvbl9wcm9wIDwtIGZ1bmN0aW9uKGZvcmVjYXN0c193aXRoX3RydXRoLGFscGhhX2xldmVsLGNvbmRpdGlvbnMsdGFyZ2V0X3R5cGU9ImRlYXRoIil7CiAgZm9yZWNhc3Rfc3ViMCA8LSBmb3JlY2FzdHNfd2l0aF90cnV0aFtncmVwbCh0YXJnZXRfdHlwZSxmb3JlY2FzdHNfd2l0aF90cnV0aCR0YXJnZXQpLF0KICBmb3JlY2FzdF9zdWJzZXQgPC0gc3Vic2V0KGZvcmVjYXN0X3N1YjAsZXZhbChjb25kaXRpb25zKSkKICBsYm91bmRzID0gc3Vic2V0KGZvcmVjYXN0X3N1YnNldCxxdWFudGlsZT09YWxwaGFfbGV2ZWwvMixzZWxlY3Q9InZhbHVlIikkdmFsdWUKICB1Ym91bmRzID0gIHN1YnNldChmb3JlY2FzdF9zdWJzZXQscXVhbnRpbGU9PTEgLSBhbHBoYV9sZXZlbC8yLHNlbGVjdD0idmFsdWUiKSR2YWx1ZQogIHRydXRoID0gIHN1YnNldChmb3JlY2FzdF9zdWJzZXQscXVhbnRpbGU9PTAuNSxzZWxlY3QgPSAidHJ1dGgiKSR0cnV0aAoKCiAgaW5fYm91bmRzIDwtICgodWJvdW5kcyAtIHRydXRoKT49MCkgJiAoKGxib3VuZHMtdHJ1dGgpPD0wKQoKICBwcm9wID0gc3VtKGluX2JvdW5kcykvbGVuZ3RoKHRydXRoKQoKICBjaGVja19kZiA8LSBjYmluZChsYm91bmRzLHVib3VuZHMsdHJ1dGgsaW5fYm91bmRzKQoKCiAgcmV0dXJuKHByb3ApCgp9CgoKCgpgYGAKCiMjIyBDYWxpYnJhdGlvbiBmaWd1cmVzCgpgYGB7ciBVUy1jYWxpYnJhdGlvbi1jYXNlc30KCmZvciAoaSBpbiAxOjQpewoKY3Vycl9sb2MgPSAiVVMiCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIixpKQpjb25kaXRpb24gPSBleHByZXNzaW9uKCh0YXJnZXQ9PWNvbmRfdGFyZ2V0KSYobG9jYXRpb249PSJVUyIpKQp0YXJfdHlwZSA9ICJjYXNlIgojIHRpdGxlX3RleHQgPC0gcGFzdGUoY3Vycl9sb2MsIi0iLHRhcl90eXBlKQp0aXRsZV90ZXh0IDwtIHNwcmludGYoIlVTIC0gJXMgd2VlayBhaGVhZCBjdW11bGF0aXZlIGNhc2VzIixpKQoKCgpjdXJyX2xvY19maXBzID0gZmlwcyhjdXJyX2xvYykKaWYgKGN1cnJfbG9jID09ICJVUyIpewogY3Vycl9sb2NfZmlwcyA9ICJVUyIKfQppZiAoY3Vycl9sb2NfZmlwcyA8IDEwKXsKICBjdXJyX2xvY19maXBzID0gcGFzdGUwKDAsY3Vycl9sb2NfZmlwcykKfWVsc2V7Y3Vycl9sb2NfZmlwcyA9IGFzLmNoYXJhY3RlcihjdXJyX2xvY19maXBzKX0KCiMgY29uZGl0aW9uID0gZXhwcmVzc2lvbigobG9jYXRpb249PWN1cnJfbG9jX2ZpcHMpKQoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvVVNfJXN3a2FoZWFkY2FzZXNfY2FsaWJyYXRpb24ucGRmIixpKQoKcGRmKGZpZ2ZpbGVuYW1lX2NhbGlicmF0aW9uLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmEzIDwtIGdncGxvdChkYXRhPWNhbGliX2RmKSsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT1hY3RfcHJvcCkpKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHhsaW0oYygwLDEpKSt5bGltKGMoMCwxKSkrCiAgeGxhYigiRXhwZWN0ZWQgUHJvcG9ydGlvbiIpKwogIHlsYWIoIkFjdHVhbCBQcm9wb3J0aW9uIikrCiAgZ2d0aXRsZSh0aXRsZV90ZXh0KQpwcmludChhMykKZGV2Lm9mZigpCn0KYGBgCgoKCmBgYHtyIFN0YXRlLWNhbGlicmF0aW9uLWNhc2VzfQoKZm9yIChpIGluIDE6NCl7CgoKCmNvbmRfdGFyZ2V0ID0gc3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGNhc2UiLGkpCmNvbmRpdGlvbiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbiE9IlVTIikpCnRhcl90eXBlID0gImNhc2UiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiU3RhdGUtbGV2ZWwgLSAlcyB3ZWVrIGFoZWFkIGN1bXVsYXRpdmUgY2FzZXMiLGkpCgoKCmFsbF9wcm9wcyA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcCA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbix0YXJfdHlwZSkKICBhbGxfcHJvcHMgPC0gYyhhbGxfcHJvcHMsY3Vycl9wcm9wKQp9CgoKY2FsaWJfZGYgPC0gZGF0YS5mcmFtZShleHBfcHJvcCA9IGFsbF9DSV93aWR0aHMsYWN0X3Byb3AgPSBhbGxfcHJvcHMpCgoKZmlnZmlsZW5hbWVfY2FsaWJyYXRpb24gPC0gc3ByaW50ZigicmVzdWx0c19maWd1cmVzL3N0YXRlLWxldmVsXyVzd2thaGVhZGNhc2VzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCmBgYHtyIFVTLWNhbGlicmF0aW9uLWRlYXRoc30KCmZvciAoaSBpbiAxOjQpewoKY3Vycl9sb2MgPSAiVVMiCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBkZWF0aCIsaSkKY29uZGl0aW9uID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uPT0iVVMiKSkKdGFyX3R5cGUgPSAiZGVhdGgiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiVVMgLSAlcyB3ZWVrIGFoZWFkIGN1bXVsYXRpdmUgZGVhdGhzIixpKQoKCgpjdXJyX2xvY19maXBzID0gZmlwcyhjdXJyX2xvYykKaWYgKGN1cnJfbG9jID09ICJVUyIpewogY3Vycl9sb2NfZmlwcyA9ICJVUyIKfQppZiAoY3Vycl9sb2NfZmlwcyA8IDEwKXsKICBjdXJyX2xvY19maXBzID0gcGFzdGUwKDAsY3Vycl9sb2NfZmlwcykKfWVsc2V7Y3Vycl9sb2NfZmlwcyA9IGFzLmNoYXJhY3RlcihjdXJyX2xvY19maXBzKX0KCiMgY29uZGl0aW9uID0gZXhwcmVzc2lvbigobG9jYXRpb249PWN1cnJfbG9jX2ZpcHMpKQoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvVVNfJXN3a2FoZWFkZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCgpgYGB7ciBTdGF0ZS1jYWxpYnJhdGlvbi1kZWF0aHN9Cgpmb3IgKGkgaW4gMTo0KXsKCgoKY29uZF90YXJnZXQgPSBzcHJpbnRmKCIlcyB3ayBhaGVhZCBjdW0gZGVhdGgiLGkpCmNvbmRpdGlvbiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbiE9IlVTIikpCnRhcl90eXBlID0gImRlYXRoIgojIHRpdGxlX3RleHQgPC0gcGFzdGUoY3Vycl9sb2MsIi0iLHRhcl90eXBlKQp0aXRsZV90ZXh0IDwtIHNwcmludGYoIlN0YXRlLWxldmVsIC0gJXMgd2VlayBhaGVhZCBjdW11bGF0aXZlIGRlYXRocyIsaSkKCgoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvc3RhdGUtbGV2ZWxfJXN3a2FoZWFkZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCgoKCgpgYGB7ciBjb21iaW5lZC1VUy1TdGF0ZS1jYWxpYnJhdGlvbi1kZWF0aHN9CgoKY2FsaWJfZGYgPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGkgaW4gMTo0KXsKCgoKY29uZF90YXJnZXQgPSBzcHJpbnRmKCIlcyB3ayBhaGVhZCBjdW0gZGVhdGgiLGkpCmNvbmRpdGlvbjEgPSBleHByZXNzaW9uKCh0YXJnZXQ9PWNvbmRfdGFyZ2V0KSYobG9jYXRpb24hPSJVUyIpKQpjb25kaXRpb24yID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uPT0iVVMiKSkKdGFyX3R5cGUgPSAiZGVhdGgiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiJXMgd2VlayBhaGVhZCIsaSkKCgoKYWxsX3Byb3BzMSA8LSBjKCkKYWxsX3Byb3BzMiA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcDEgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24xLHRhcl90eXBlKQogIGFsbF9wcm9wczEgPC0gYyhhbGxfcHJvcHMxLGN1cnJfcHJvcDEpCiAgCiAgY3Vycl9wcm9wMiA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbjIsdGFyX3R5cGUpCiAgYWxsX3Byb3BzMiA8LSBjKGFsbF9wcm9wczIsY3Vycl9wcm9wMikKfQoKCnRlbXAxIDwtIGRhdGEuZnJhbWUoZXhwX3Byb3AgPSBhbGxfQ0lfd2lkdGhzLGFjdF9wcm9wID0gYWxsX3Byb3BzMSxsZXZlbD0ic3RhdGUiLHRhcmdldD10aXRsZV90ZXh0KQp0ZW1wMiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wczIsbGV2ZWw9IlVTIix0YXJnZXQ9dGl0bGVfdGV4dCkKY2FsaWJfZGYgPC0gcmJpbmQoY2FsaWJfZGYsdGVtcDEsdGVtcDIpCgp9CgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA3LCBoZWlnaHQgPSA0KQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3Asc2hhcGU9bGV2ZWwsY29sb3I9bGV2ZWwpLGNleD0xLjUpKwogICMgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9dXNfcHJvcCxzaGFwZT0iYiIpLHNoYXBlPTE5KSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUoIlBJIENhcHR1cmUgUmF0ZXMgZm9yIEN1bXVsYXRpdmUgRGVhdGggRm9yZWNhc3RzIikrCiAgZmFjZXRfd3JhcChmYWNldHM9dmFycyh0YXJnZXQpLG5yb3c9MikKCnByaW50KGEzKQpkZXYub2ZmKCkKcHJpbnQoYTMpCgpgYGAKCgoKCmBgYHtyIGNvbWJpbmVkLVVTLVN0YXRlLWNhbGlicmF0aW9uLWNhc2VzfQoKCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiAxOjQpewoKCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIixpKQpjb25kaXRpb24xID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uIT0iVVMiKSkKY29uZGl0aW9uMiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbj09IlVTIikpCnRhcl90eXBlID0gImNhc2UiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiJXMgd2VlayBhaGVhZCIsaSkKCgoKYWxsX3Byb3BzMSA8LSBjKCkKYWxsX3Byb3BzMiA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcDEgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24xLHRhcl90eXBlKQogIGFsbF9wcm9wczEgPC0gYyhhbGxfcHJvcHMxLGN1cnJfcHJvcDEpCiAgCiAgY3Vycl9wcm9wMiA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbjIsdGFyX3R5cGUpCiAgYWxsX3Byb3BzMiA8LSBjKGFsbF9wcm9wczIsY3Vycl9wcm9wMikKfQoKCnRlbXAxIDwtIGRhdGEuZnJhbWUoZXhwX3Byb3AgPSBhbGxfQ0lfd2lkdGhzLGFjdF9wcm9wID0gYWxsX3Byb3BzMSxsZXZlbD0ic3RhdGUiLHRhcmdldD10aXRsZV90ZXh0KQp0ZW1wMiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wczIsbGV2ZWw9IlVTIix0YXJnZXQ9dGl0bGVfdGV4dCkKY2FsaWJfZGYgPC0gcmJpbmQoY2FsaWJfZGYsdGVtcDEsdGVtcDIpCgp9CgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvY2FzZXNfY2FsaWJyYXRpb24ucGRmIixpKQoKcGRmKGZpZ2ZpbGVuYW1lX2NhbGlicmF0aW9uLCB3aWR0aCA9IDcsIGhlaWdodCA9IDQpCmEzIDwtIGdncGxvdChkYXRhPWNhbGliX2RmKSsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT1hY3RfcHJvcCxzaGFwZT1sZXZlbCxjb2xvcj1sZXZlbCksY2V4PTEuNSkrCiAgIyBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT11c19wcm9wLHNoYXBlPSJiIiksc2hhcGU9MTkpKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHhsaW0oYygwLDEpKSt5bGltKGMoMCwxKSkrCiAgeGxhYigiRXhwZWN0ZWQgUHJvcG9ydGlvbiIpKwogIHlsYWIoIkFjdHVhbCBQcm9wb3J0aW9uIikrCiAgZ2d0aXRsZSgiUEkgQ2FwdHVyZSBSYXRlcyBmb3IgQ3VtdWxhdGl2ZSBDYXNlIEZvcmVjYXN0cyIpKwogIGZhY2V0X3dyYXAoZmFjZXRzPXZhcnModGFyZ2V0KSxucm93PTIpCgpwcmludChhMykKZGV2Lm9mZigpCnByaW50KGEzKQoKYGBgCg==